{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# JavaScript 函数定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数声明"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "function myFunction(a, b) {\n",
    "    return a * b;\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "312"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "myFunction(13,24);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数表达式(匿名函数)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# var x = function(a, b) {return a * b};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2408"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x(43,56);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Function() 构造函数\n",
    "函数同样可以通过内置的 JavaScript 函数构造器（Function()）定义。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n"
     ]
    }
   ],
   "source": [
    "var myFunction = new Function(\"a\", \"b\", \"return a * b\");\n",
    "\n",
    "var x = myFunction(4, 3);\n",
    "console.log(x);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "实际上，你不必使用构造函数。上面实例可以写成："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "12\n"
     ]
    }
   ],
   "source": [
    "var myFunction = function (a, b) {return a * b};\n",
    "\n",
    "var x = myFunction(4, 3);\n",
    "console.log(x);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数提升（Hoisting）\n",
    "在之前的教程中我们已经了解了 \"hoisting(提升)\"。\n",
    "\n",
    "提升（Hoisting）是 JavaScript 默认将当前作用域提升到前面去的的行为。\n",
    "\n",
    "提升（Hoisting）应用在变量的声明与函数的声明。\n",
    "\n",
    "**使用表达式定义函数时无法提升。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 自调用函数(匿名函数)\n",
    "函数表达式可以 \"自调用\"。\n",
    "\n",
    "自调用表达式会自动调用。\n",
    "\n",
    "如果表达式后面紧跟 () ，则会自动调用。\n",
    "\n",
    "不能自调用声明的函数。\n",
    "\n",
    "通过添加括号，来说明它是一个函数表达式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello!!\n"
     ]
    }
   ],
   "source": [
    "(function () {\n",
    "    var x = \"Hello!!\";      // 我将调用自己\n",
    "    console.log(x);\n",
    "})();"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "匿名箭头函数自调用\n"
     ]
    }
   ],
   "source": [
    "(()=>{console.log(\"匿名箭头函数自调用\")})()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数可作为一个值&表达式使用"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "42\n",
      "24\n"
     ]
    }
   ],
   "source": [
    "function myFunction(a, b) {\n",
    "    return a * b;\n",
    "}\n",
    "\n",
    "var y = myFunction(6, 7);\n",
    "var x = myFunction(4, 3) * 2;\n",
    "\n",
    "console.log(y);\n",
    "console.log(x);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 函数是对象\n",
    "在 JavaScript 中使用 **typeof** 操作符判断函数类型将返回 \"function\" 。\n",
    "\n",
    "但是JavaScript 函数描述为一个对象更加准确。\n",
    "\n",
    "JavaScript 函数有 **属性** 和 **方法**。\n",
    "\n",
    "`arguments.length` 属性返回函数调用过程接收到的参数个数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n"
     ]
    }
   ],
   "source": [
    "function myFunction(a, b) {\n",
    "    return arguments.length;\n",
    "}\n",
    "\n",
    "console.log(myFunction(1,2,3,4,5,6));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`toString()` 方法将函数作为一个字符串返回:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "function myFunction(a, b) {\n",
      "    return a * b;\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "function myFunction(a, b) {\n",
    "    return a * b;\n",
    "}\n",
    "\n",
    "var txt = myFunction.toString();\n",
    "console.log(txt);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**函数定义作为对象的属性，称之为对象方法。**\n",
    "\n",
    "**函数如果用于创建新的对象，称之为对象的构造函数。**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 补充：arguments 对象\n",
    "JavaScript 函数有个内置的对象 arguments 对象。\n",
    "\n",
    "argument 对象包含了函数调用的参数数组。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "500"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = findMax(1, 123, 500, 115, 44, 88);\n",
    " \n",
    "function findMax() {\n",
    "    var i, max = arguments[0];\n",
    "    \n",
    "    if(arguments.length < 2) return max;\n",
    " \n",
    "    for (i = 0; i < arguments.length; i++) {\n",
    "        if (arguments[i] > max) {\n",
    "            max = arguments[i];\n",
    "        }\n",
    "    }\n",
    "    return max;\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 箭头函数\n",
    "ES6 新增了箭头函数。\n",
    "\n",
    "箭头函数表达式的语法比普通函数表达式更简洁。\n",
    "```js\n",
    "(参数1, 参数2, …, 参数N) => { 函数声明 }\n",
    "\n",
    "(参数1, 参数2, …, 参数N) => 表达式  //单一，相当于\n",
    "(参数1, 参数2, …, 参数N) =>{ return 表达式; }\n",
    "```\n",
    "\n",
    "当只有一个参数时，圆括号是可选的：\n",
    "```js\n",
    "(单一参数) => {函数声明}\n",
    "单一参数 => {函数声明}\n",
    "```\n",
    "\n",
    "没有参数的函数应该写成一对圆括号:\n",
    "```js\n",
    "() => {函数声明}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [],
   "source": [
    "// ES5\n",
    "var x = function(x, y) {\n",
    "     return x * y;\n",
    "}\n",
    " \n",
    "// ES6\n",
    "const k = (x, y) => x * y;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "6\n",
      "20\n"
     ]
    }
   ],
   "source": [
    "console.log(x(2,3));\n",
    "console.log(k(4,5));"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "有的箭头函数都没有自己的 **this**。 不适合定义一个 **对象的方法**。\n",
    "\n",
    "当我们使用箭头函数的时候，箭头函数会默认帮我们绑定外层 this 的值，所以在箭头函数中 this 的值和外层的 this 是一样的。\n",
    "\n",
    "**箭头函数是不能提升的，所以需要在使用之前定义。**\n",
    "\n",
    "使用 const 比使用 var 更安全，因为函数表达式始终是一个常量。\n",
    "\n",
    "如果函数部分只是一个语句，则可以省略 return 关键字和大括号 {}，这样做是一个比较好的习惯:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [],
   "source": [
    "const j = (x, y) => x * y ;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "20\n"
     ]
    }
   ],
   "source": [
    "console.log(j(4,5));"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Javascript (Node.js)",
   "language": "javascript",
   "name": "javascript"
  },
  "language_info": {
   "file_extension": ".js",
   "mimetype": "application/javascript",
   "name": "javascript",
   "version": "13.13.0"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
